<?php
namespace WDB\Analyzer;
use WDB,
    WDB\Exception;

/**
 *
 * @author Richard Ejem <richard(at)ejem.cz>
 * @package WDB
 */
class Column extends WDB\BaseObject implements iColumn
{
    /**@var string*/
    protected $name;
    /**@var iTable*/
    protected $tableAnalyzer;
    /**@var WDB\Structure\ColumnMetaInfo*/
    protected $meta;
    /**@var ForeignKeyData[]*/
    protected $foreign = array();
    /**@var string*/
    protected $datatype = 'String';
    /**@var \WDB\Annotation\Reflection*/
    protected $annotations;

    /**
     * Returns true if this column is an AUTO_INCREMENT. iColumn orders only to specify is
     *
     * @return bool
     */
    public function isAutoIncrement()
    {
        return $this->meta->isAutoIncrement;
    }

    public function __construct($name, TableView $table, WDB\Structure\ColumnMetaInfo $meta)
    {
        $this->name = $name;
        $this->tableAnalyzer = $table;
        $this->meta = $meta;
        $this->annotations = WDB\Annotation\Reflection::fromString($meta->comment);
        $this->annotations->mergeNeonWheel($table->getSchema()->getModel(), $table->getName().'.'.$this->name);
        if ($this->wrapperClass !== NULL)
        {
            $refl = new \ReflectionClass($this->wrapperClass);
            $this->annotations->merge($refl->getDocComment());
        }
    }

    // <editor-fold defaultstate="collapsed" desc="iColumn implementation">
    public function isAutoColumn()
    {
        return isset($this->annotations->auto) || $this->meta->isAutoColumn;
    }

    public function getWrapperClass()
    {
        if (isset($this->annotations->wrapper))
        {
            return WDB\Utils\System::findClass($this->annotations->last('wrapper')->text, WDB\Config::read('userWrappersClassPrefix'));
        }
        return NULL;
    }
    public function getWebUIClass()
    {
        $userWCP = WDB\Config::read('userWebUIClassPrefix');
        if (isset($this->annotations->webui))
        {
            $class = $this->annotations->last('webui')->getText();
            if (class_exists($userWCP.$class)) return $userWCP.$class;
            if (class_exists($class)) return $class;
            throw new Exception\ClassNotFound("WebUI class {$class} for column {$this->name} not found (set by db comment annotation)");
        }
        return NULL;
    }

    public function getWebUIDisplayMode($original = NULL)
    {
        $display = ($original === NULL ? ~0 : $original);
        $this->parseModes($display, $this->table->getDisplayDefault());
        $this->parseModes($display, $this->annotations->display);
        return $display;
    }

    public function getAnnotations()
    {
        return $this->annotations;
    }

    public function getDefault()
    {
        return $this->meta->default;
    }

    public function getIdentifier()
    {
        return WDB\Query\Element\ColumnIdentifier::create(
                $this->name,
                $this->tableAnalyzer->getName(),
                $this->tableAnalyzer->getSchema()->getName());
    }
    public function getName()
    {
        return $this->name;
    }
    public function getTitle()
    {
        return isset($this->annotations->title) ? $this->annotations->last('title')->getText() : $this->name;
    }
    public function getTable()
    {
        return $this->tableAnalyzer;
    }
    public function isNullable()
    {
        return $this->meta->nullable;
    }

    public function getForeignKeys()
    {
        if (!$this->tableAnalyzer instanceof Table) return array();
        $keys = array();
        foreach ($this->foreign as $f)
        {
            $keys[] = $this->tableAnalyzer->foreignKeys[$f];
        }
        return $keys;
    }

    public function fetchValidationRules(WDB\Validation\ColumnRules $rules)
    {
        //just a placeholder for common validation rules for all column types
    }

    /**
     * highly recommended to override in children to create datatype of the column's actual data type.
     * @param mixed
     * @return WDB\Query\Element\Datatype\iDatatype
     */
    public function valueToDatatype($value)
    {
        return WDB\Query\Element\Datatype\AbstractType::createDatatype($value);
    }

    public function setFieldValue($value)
    {
        return $value;
    }

    public function getStringValue($value)
    {
        return $value;
    }
    // </editor-fold>

    /**
     * tells that this column is (part of) foreign key with identifier $name
     *
     * @param string foreign key identifier
     */
    public function addForeignKey($name)
    {
        $this->foreign[] = $name;
    }

    /**
     * Updates display mode bitmask with configuration string in form of ([+|-]{list|detail|edit|editread|editwrite} )*
     *
     * @param int original display mode bitmask
     * @param array modes to mask
     * @return int updated display mode bitmask
     * @throws Exception\BadArgument
     */
    private function parseModes(&$original, array $new)
    {
        foreach ($new as $modes)
        {
            $mode = explode(' ', $modes);
            foreach($mode as $m)
            {
                if ($m[0] == '-')
                {
                    $add = 0;
                    $mode = substr($m, 1);
                }
                elseif ($m[0] == '+')
                {
                    $add = 1;
                    $mode = substr($m, 1);
                }
                else
                {
                    $add = 1;
                    $mode = $m;
                }
                switch (strtolower($mode))
                {
                    case 'list':
                        $mask = WDB\WebUI\iColumn::DISPLAY_LIST;
                        break;
                    case 'detail':
                        $mask = WDB\WebUI\iColumn::DISPLAY_DETAIL;
                        break;
                    case 'edit':
                        $mask = WDB\WebUI\iColumn::DISPLAY_EDIT;
                        break;
                    case 'editread':
                        $mask = WDB\WebUI\iColumn::DISPLAY_EDIT_READONLY;
                        break;
                    case 'editwrite':
                        $mask = WDB\WebUI\iColumn::DISPLAY_EDIT &~ WDB\WebUI\iColumn::DISPLAY_EDIT_READONLY;
                        break;
                    default:
                        throw new Exception\BadArgument("unknown displayMode $mode");
                }
                if ($add)
                {
                    $original |= $mask;
                }
                else
                {
                    $original &= ~$mask;
                }
            }
        }
    }
}